package com.nervenets.shopping.service.impl;

import com.github.binarywang.wxpay.bean.order.WxPayAppOrderResult;
import com.github.binarywang.wxpay.bean.order.WxPayMpOrderResult;
import com.github.binarywang.wxpay.bean.order.WxPayNativeOrderResult;
import com.github.binarywang.wxpay.bean.request.WxPayUnifiedOrderRequest;
import com.github.binarywang.wxpay.constant.WxPayConstants;
import com.github.binarywang.wxpay.exception.WxPayException;
import com.github.binarywang.wxpay.service.WxPayService;
import com.nervenets.general.config.ApplicationProperties;
import com.nervenets.general.enumeration.Action;
import com.nervenets.general.exception.LogicException;
import com.nervenets.general.jwt.util.JwtUtils;
import com.nervenets.general.service.impl.BaseServiceImpl;
import com.nervenets.general.utils.HttpTools;
import com.nervenets.general.utils.JodaUtils;
import com.nervenets.general.web.params.Params;
import com.nervenets.shopping.enumeration.OrderStatus;
import com.nervenets.shopping.enumeration.PayType;
import com.nervenets.shopping.enumeration.WealthType;
import com.nervenets.shopping.exception.WalletOpenException;
import com.nervenets.shopping.hibernate.dao.GroupBuyOrderUserDao;
import com.nervenets.shopping.hibernate.dao.vo.DayCount;
import com.nervenets.shopping.hibernate.domain.common.GlobalRuleSetting;
import com.nervenets.shopping.hibernate.domain.order.GroupBuyOrder;
import com.nervenets.shopping.hibernate.domain.order.GroupBuyOrderUser;
import com.nervenets.shopping.hibernate.domain.user.UserWxId;
import com.nervenets.shopping.service.*;
import lombok.SneakyThrows;
import org.springframework.stereotype.Service;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.List;
import java.util.Objects;

@Service
public class GroupBuyOrderUserServiceImpl extends BaseServiceImpl<GroupBuyOrderUser, GroupBuyOrderUserDao> implements GroupBuyOrderUserService {
    @Resource
    private WxPayService wxPayService;
    @Resource
    private ApplicationProperties applicationProperties;
    @Resource
    private UserService userService;
    @Resource
    private WealthFlowService wealthFlowService;
    @Resource
    private GroupBuyOrderService groupBuyOrderService;
    @Resource
    private GlobalRuleService globalRuleService;

    @Override
    protected void before(Action action, GroupBuyOrderUser groupBuyOrderUser, Params... params) {
        super.before(action, groupBuyOrderUser, params);
        if (Action.add.equals(action)) {
            GlobalRuleSetting setting = globalRuleService.getGlobalRule().getSetting();
            if (count((predicates, root, query, builder) -> {
                predicates.add(builder.equal(root.get("createBy"), JwtUtils.getUser().getId()));
                predicates.add(builder.between(root.get("createTime"), JodaUtils.getStartOfToday(), JodaUtils.getEndOfToday()));
            }) >= setting.getGroupBuyTimes()) {
                throw new WalletOpenException(String.format("每人每天最多发起参与拼团%s次，（提交未支付的也计数）请珍惜机会，邀请您的朋友来谋福利吧。", setting.getGroupBuyTimes()));
            }

            GroupBuyOrder groupBuy = groupBuyOrderUser.getProduct().getCurrentGroupBuy();
            if (null != groupBuy) {
                List<GroupBuyOrderUser> users = groupBuy.getUsers();
                for (GroupBuyOrderUser user : users) {
                    if (groupBuyOrderUser.getCreateBy() == user.getCreateBy()) {
                        throw new LogicException("当前拼团您已成功加入，无需重复发起拼团。");
                    }
                }
            }

            if (groupBuyOrderUser.getSubsidy() > 0) {
                long surplus = wealthFlowService.getSurplus(JwtUtils.getUser().getId(), WealthType.money);
                if (surplus < groupBuyOrderUser.getSubsidy()) {
                    throw new LogicException("抵扣额度不能大于用户的余额！");
                }
            }

            if (groupBuyOrderUser.getMoney() == 0) {
                groupBuyOrderUser.setPayType(PayType.surplus);
                groupBuyOrderUser.setStatus(OrderStatus.paid);
                groupBuyOrderUser.setPayTime(JodaUtils.getTimestamp());
            }
        }
    }

    @SneakyThrows
    @Override
    protected void after(Action action, GroupBuyOrderUser groupBuyOrderUser, Params... params) {
        super.after(action, groupBuyOrderUser, params);
        if (Action.add.equals(action)) {
            if (groupBuyOrderUser.getMoney() > 0) {
                final String notifyUrl = String.format("http://%s/api/groupbuy/order/user/wx/callback", applicationProperties.getBaseDomain());
                UserWxId userWxId = userService.findUserWxId(userService.findOne(groupBuyOrderUser.getCreateBy()), wxPayService.getConfig().getAppId());
                if (null == userWxId) throw new LogicException("您还没有授权登录。");
                groupBuyOrderUser.setThirdPayInfo(wxJsapiPay(
                        groupBuyOrderUser.getProductName().getBytes().length > 128 ? "拼团商品" : groupBuyOrderUser.getProductName(),
                        groupBuyOrderUser.getTradeNo(),
                        groupBuyOrderUser.getMoney(),
                        userWxId.getOpenId(),
                        notifyUrl,
                        ((ServletRequestAttributes) Objects.requireNonNull(RequestContextHolder.getRequestAttributes())).getRequest()
                ));
            } else {
                groupBuyOrderService.join(groupBuyOrderUser);
            }
        }
    }

    protected WxPayNativeOrderResult wxNativePay(String body, String orderNumber, long money, String productId, String notifyUrl, HttpServletRequest request) throws WxPayException {
        final WxPayUnifiedOrderRequest unifiedOrderRequest = WxPayUnifiedOrderRequest.newBuilder()
                .body(body)
                .productId(productId)
                .outTradeNo(orderNumber)
                .totalFee(Math.toIntExact(money))
                .spbillCreateIp(HttpTools.getIpAddress(request))
                .notifyUrl(notifyUrl)
                .tradeType(WxPayConstants.TradeType.NATIVE)
                .build();

        return wxPayService.createOrder(unifiedOrderRequest);
    }

    @Override
    public WxPayMpOrderResult wxJsapiPay(String body, String orderNumber, long money, String openId, String notifyUrl, HttpServletRequest request) throws WxPayException {
        final WxPayUnifiedOrderRequest unifiedOrderRequest = WxPayUnifiedOrderRequest.newBuilder()
                .body(body)
                .outTradeNo(orderNumber)
                .totalFee(Math.toIntExact(money))
                .spbillCreateIp(HttpTools.getIpAddress(request))
                .notifyUrl(notifyUrl)
                .tradeType(WxPayConstants.TradeType.JSAPI)
                .openid(openId)
                .build();
        return wxPayService.createOrder(unifiedOrderRequest);
    }

    @Override
    public long countUserState(long userId, long startTime, long endTime) {
        return dao.countUserState(userId, startTime, endTime);
    }

    @Override
    public long sumOfReward() {
        return dao.sumOfReward().longValue();
    }

    @Override
    public long sumOfReward(long startTime, long endTime) {
        return dao.sumOfReward(startTime, endTime).longValue();
    }

    @Override
    public long sumOfOrderMoney() {
        return dao.sumOfOrderMoney().longValue();
    }

    @Override
    public long sumOfOrderMoney(long startTime, long endTime) {
        return dao.sumOfOrderMoney(startTime, endTime).longValue();
    }

    @Override
    public long sumOfRefundMoney() {
        return dao.sumOfRefundMoney().longValue();
    }

    @Override
    public long sumOfRefundMoney(long startTime, long endTime) {
        return dao.sumOfRefundMoney(startTime, endTime).longValue();
    }

    @Override
    public List<DayCount> groupByDay(long userId, long startTime, long endTime) {
        return dao.groupByDay(userId, startTime, endTime);
    }

    @Override
    public List<GroupBuyOrderUser> findTopUngroupedOrder(long timeout) {
        return dao.findUngroupedOrder(timeout);
    }

    protected WxPayAppOrderResult wxAppPay(String body, String orderNumber, long money, String notifyUrl, HttpServletRequest request) throws WxPayException {
        final WxPayUnifiedOrderRequest unifiedOrderRequest = WxPayUnifiedOrderRequest.newBuilder()
                .body(body)
                .outTradeNo(orderNumber)
                .totalFee(Math.toIntExact(money))
                .spbillCreateIp(HttpTools.getIpAddress(request))
                .notifyUrl(notifyUrl)
                .tradeType(WxPayConstants.TradeType.APP)
                .build();
        return wxPayService.createOrder(unifiedOrderRequest);
    }
}
